/*
 * @Author       : Deusit && 505097558@qq.com
 * @Date         : 2023-06-05 23:02:55
 * @LastEditors  : Deusit && 505097558@qq.com
 * @LastEditTime : 2023-08-13 18:53:21
 * @Description  : file content
 * @FilePath     : deusitWifi.cpp
 * Copyright (c) 2023 by Deusit email: 505097558@qq.com, All Rights Reserved.
 */
#include "deusitWifi.h"

#include <ArduinoJson.h>
#include <DNSServer.h>
#include <LittleFS.h>
#include <variant>

#define deusitWifiVersion "1.0.0"
#define WiFi_State_Addr 0



const byte DNS_PORT = 53;

int WiFi_State;     // 0=未配置WIFI;1=已配置WIFI;2=隐藏WIFI
int rstb = 0;
int stateled = 2;

// 过滤信号强度
int Signal_filtering = -200;

/*char* deviceName = "Deusit_ESP8266";
char* hostName = "Deusit_ESP8266";
char* APName = "Deusit_WIFI";*/
const char* DEUSIT_WIFI::deviceName;
const char* DEUSIT_WIFI::hostName;
const char* DEUSIT_WIFI::APName;

IPAddress apIP(6, 6, 6, 6);
ESP8266WebServer webServer(80);
DNSServer dnsServer;

/**
 * @brief JSON输出
 * 
 * @param content JsonDocument JSON体,最大10KB
 */
void jsonSend(int status, String msg="获取成功", DynamicJsonDocument doc=DynamicJsonDocument (10240))
{
    String content;
    DynamicJsonDocument returnDoc(10240);
    returnDoc["status"] = status;
    returnDoc["msg"] = msg;
    if(!doc.isNull()) returnDoc["data"] = doc;
    serializeJson(returnDoc, content);
    Serial.println("\n============================\nHTTP返回：");
    serializeJsonPretty(returnDoc, Serial);
    Serial.println("\n============================");

    webServer.sendHeader("Access-Control-Allow-Origin", "*");
    webServer.sendHeader("Access-Control-Max-Age", "10000");
    webServer.sendHeader("Access-Control-Allow-Methods", "PUT,POST,GET,OPTIONS");
    webServer.sendHeader("Access-Control-Allow-Headers", "*");
    webServer.send(200, "text/json;charset=UTF-8", content);
}

String DEUSIT_WIFI::readConfig(String key="")
{
    if(!LittleFS.exists("/config.json")){
        return "";
    }
    File file = LittleFS.open("/config.json", "r");
    if(!file){
        Serial.println("打开配置文件失败！");
        return "";
    }
    String configStr = file.readString();
    file.close();
    if(key.isEmpty()){
        return configStr;
    }
    DynamicJsonDocument doc(configStr.length()*2);
    deserializeJson(doc, configStr);
    return doc[key];
}

void DEUSIT_WIFI::writeConfig(String key = "", std::variant<String, int, float> val = "")
{
    String configStr = readConfig();
    DynamicJsonDocument doc(512);
    deserializeJson(doc, configStr);
    if (std::holds_alternative<String>(val)) {
        doc[key] = std::get<String>(val);
    } else if (std::holds_alternative<int>(val)) {
        doc[key] = std::get<int>(val);
    }
    configStr.clear();
    serializeJson(doc, configStr);

    File file = LittleFS.open("/config.json", "w");
    if (!file) {
        Serial.println("写入配置文件失败！");
        return;
    }
    if (key.isEmpty()) {
        file.print("");
        file.close();
        return;
    }
    file.print(configStr);
    file.close();
}

void DEUSIT_WIFI::handleFileRead(String path) {
    if (path.endsWith("/")) path += "index.html.gz";
    String contentType = getContentType(path);
    if (LittleFS.exists(path)) {
        File file = LittleFS.open(path, "r");
        size_t sent = 0;
        size_t fileSize = file.size();
        size_t bufferSize = 2048;
        uint8_t buffer[bufferSize];
        while (file.available() && sent < fileSize) {
            size_t bytesRead = file.read(buffer, bufferSize);
            webServer.sendContent_P((const char*)buffer, bytesRead);
            sent += bytesRead;
        }
        file.close();
    } else {
        webServer.send(404, "text/plain", "404");
    }
}

void DEUSIT_WIFI::deviceInfo(bool returnJson=false)
{
    DynamicJsonDocument doc(10240);
    doc["deviceName"] = this->deviceName?this->deviceName:"Deusit_Esp8266";
    doc["bootTime"] = millis();
    doc["deusitWifiVersion"] = String(deusitWifiVersion);
    doc["wifiMode"] = String(WiFi.getMode()==WIFI_OFF?"OFF":WiFi.getMode()==WIFI_STA?"STA":WiFi.getMode()==WIFI_AP?"AP":WiFi.getMode()==WIFI_AP_STA?"AP_STA":"ERROR");
    doc["wifiState"] = WiFi_State;
    doc["wifiHide"] = WiFi_State==2?1:0;
    if(WiFi_State > 0 && WiFi.status() == WL_CONNECTED){
        doc["wifiSSID"] = String(WiFi.SSID());
        doc["wifiRSSI"] = String(WiFi.RSSI());
        doc["wifiLocalIP"] = WiFi.localIP();
    }
    
    doc["coreVersion"] = String(ESP.getCoreVersion());
    doc["sdkVersion"] = String(ESP.getSdkVersion());
    doc["chipId"] = String(ESP.getChipId());
    doc["macAddress"] = String(WiFi.macAddress().c_str());
    doc["softAPmacAddress"] = String(WiFi.softAPmacAddress().c_str());
    doc["freeHeap"] = String(ESP.getFreeHeap()/1024)+"KB";
    doc["cpuFreqMHz"] = String(ESP.getCpuFreqMHz())+"MHZ";
    doc["sketchSize"] = String(ESP.getSketchSize()/1024)+"KB";
    doc["freeSketchSpace"] = String(ESP.getFreeSketchSpace()/1024)+"KB";
    doc["flashChipId"] = String(ESP.getFlashChipId());
    FlashMode_t flash_Mode = ESP.getFlashChipMode();
    doc["flashChipMode"] = flash_Mode == FM_QIO ? "QIO" : flash_Mode == FM_QOUT ? "QOUT" : flash_Mode == FM_DIO ? "DIO" : flash_Mode == FM_DOUT ? "DOUT" : "UNKNOWN";
    doc["flashChipRealSize"] = String(ESP.getFlashChipRealSize()/1024)+"KB";
    doc["flashChipSpeed"] = String(ESP.getFlashChipSpeed()/1000000)+"MHZ";

    String configStr = readConfig();
    DynamicJsonDocument configDoc(512);
    deserializeJson(configDoc, configStr);
    
    if(configDoc["mqtt_host"]){
        doc["mqtt"]["host"] = configDoc["mqtt_host"];
        doc["mqtt"]["port"] = configDoc["mqtt_port"];
        doc["mqtt"]["path"] = configDoc["mqtt_path"];
        doc["mqtt"]["username"] = configDoc["mqtt_username"];
        doc["mqtt"]["password"] = configDoc["mqtt_password"];
        doc["mqtt"]["topic"] = configDoc["mqtt_topic"];
        doc["mqtt"]["retain"] = configDoc["mqtt_retain"];
        doc["mqtt"]["status"] = configDoc["mqtt_retain"];
    }
    
    Serial.println("\n>>>>>>>>>>>>>>>>>>>>系统信息<<<<<<<<<<<<<<<<<<<<");
    Serial.println("|-- 设备名称：" + String(doc["deviceName"]));
    Serial.println("|-- 开机时间：" + String(doc["bootTime"])+"ms");
    Serial.println("|-- WIFI模式：" + String(doc["wifiMode"]));
    Serial.println("|-- WIFI状态：" + String(WiFi_State==0?"未连接":WiFi_State==1?"已连接":"已连接，隐藏AP"));
    if(WiFi_State > 0 && WiFi.status() == WL_CONNECTED){
        Serial.println("|-- SSID：" + String(doc["wifiSSID"]));
        Serial.println("|-- RSSI：" + String(doc["wifiRSSI"]));
        Serial.println("|-- IP地址：" + String(doc["wifiLocalIP"]));
    }
    Serial.println("|-- 固件版本：" + String(doc["deusitWifiVersion"]));
    Serial.println("|-- 核心库版本：" + String(doc["coreVersion"]));
    Serial.println("|-- SDK版本：" + String(doc["sdkVersion"]));
    Serial.println("|-- 芯片ID：" + String(doc["chipId"]));
    Serial.println("|-- 芯片STA-MAC地址：" + String(doc["macAddress"]));
    Serial.println("|-- 芯片AP-MAC地址：" + String(doc["softAPmacAddress"]));
    Serial.println("|-- 可用堆大小：" + String(doc["freeHeap"]));
    Serial.println("|-- CPU运行频率：" + String(doc["cpuFreqMHz"]));
    Serial.println("|-- 当前固件大小：" + String(doc["sketchSize"]));
    Serial.println("|-- 剩余可用固件空间：" + String(doc["freeSketchSpace"]));
    Serial.println("|-- 闪存芯片ID：" + String(doc["flashChipId"]));
    Serial.println("|-- 闪存芯片模式：" + String(doc["flashChipMode"]));
    Serial.println("|-- 闪存芯片真正大小：" + String(doc["flashChipRealSize"]));
    Serial.println("|-- 闪存芯片运行频率：" + String(doc["flashChipSpeed"]));
    
    if(doc["mqtt"]["host"]){
        Serial.println("|-- MQTT参数：");
        Serial.println("|---- 地址：" + String(doc["mqtt"]["host"]));
        Serial.println("|---- 端口：" + String(doc["mqtt"]["port"]));
        Serial.println("|---- 路径：" + String(doc["mqtt"]["path"]));
        Serial.println("|---- 用户名：" + String(doc["mqtt"]["username"]));
        Serial.println("|---- 密码：" + String(doc["mqtt"]["password"]));
        Serial.println("|---- 订阅主题：" + String(doc["mqtt"]["topic"]));
        Serial.println("|---- 保持：" + String(doc["mqtt"]["retain"]));
    }
    Serial.println(">>>>>>>>>>>>>>>>>>>>系统信息<<<<<<<<<<<<<<<<<<<<\n");

    if(returnJson) {
        jsonSend(1,"获取成功",doc);
    }
}

void DEUSIT_WIFI::wwwroot()
{
    File file = LittleFS.open("/index.html.gz", "r");
    webServer.streamFile(file, "text/html");
    file.close();
}

String DEUSIT_WIFI::wifi_type(int typecode)
{
    if (typecode == ENC_TYPE_NONE)
        return "Open";
    if (typecode == ENC_TYPE_WEP)
        return "WEP ";
    if (typecode == ENC_TYPE_TKIP)
        return "WPA ";
    if (typecode == ENC_TYPE_CCMP)
        return "WPA2";
    if (typecode == ENC_TYPE_AUTO)
        return "WPA*";
    return "";
}

String DEUSIT_WIFI::getContentType(String filename) {
    if(filename.endsWith(".html")) return "text/html";
    else if(filename.endsWith(".css")) return "text/css";
    else if(filename.endsWith(".js")) return "application/javascript";
    else if(filename.endsWith(".png")) return "image/png";
    else if(filename.endsWith(".gif")) return "image/gif";
    else if(filename.endsWith(".jpg")) return "image/jpeg";
    else if(filename.endsWith(".ico")) return "image/x-icon";
    else if(filename.endsWith(".ttf")) return "application/octet-stream";
    else if(filename.endsWith(".gz")) return "text/html";
    return "text/plain";
}

void DEUSIT_WIFI::wifiScan()
{
    DynamicJsonDocument doc(10240);
    Serial.println("扫描 WiFi");
    int n = WiFi.scanNetworks();
    int m = 0;
    /*if (n > 0)
    {
        int* signal_strengths = new int[n];
        for (int i = 0; i < n; i++)
        {
            signal_strengths[i] = (int)WiFi.RSSI(i);
        }

        qsort(signal_strengths, n, sizeof(int), [](const void* a, const void* b) -> int {
            return (*(int*)b - *(int*)a);
        });

        for (int i = 0; i < n; i++)
        {
            if (signal_strengths[i] >= Signal_filtering)
                m++;
            
            JsonObject wifi = doc.createNestedObject();
            wifi["ssid"] = String(WiFi.SSID(i));
            wifi["encryptionType"] = wifi_type(WiFi.encryptionType(i));
            wifi["rssi"] = signal_strengths[i];
            wifi["mac"] = String(WiFi.BSSIDstr(i));
        }
        delete[] signal_strengths;
    }
    jsonSend(1,"获取成功",doc);*/
    Serial.println("Found "+String(n)+" WiFi! > "+String(Signal_filtering)+"dB");
}

void DEUSIT_WIFI::wifiConfig()
{
    if (webServer.hasArg("ssid") && webServer.hasArg("password")) {
		int ssid_len = webServer.arg("ssid").length();
		int password_len = webServer.arg("password").length();
		//if ((ssid_len > 0) && (ssid_len < 33) && (password_len > 7) && (password_len < 65)) {
		if ((ssid_len > 0) && (ssid_len < 33) && (password_len < 65)) {
			String ssid_str = webServer.arg("ssid");
			String password_str = webServer.arg("password");
			const char *ssid = ssid_str.c_str();
			const char *password = password_str.c_str();
			WiFi.mode(WIFI_AP_STA);
			WiFi.disconnect(true);
			Serial.print("SSID: ");
			Serial.println(ssid);
			Serial.print("Password: ");
			Serial.println(password);
			WiFi.begin(ssid, password);
			Serial.print("尝试连接");
			unsigned long millis_time = millis();
			while ((WiFi.status() != WL_CONNECTED) && (millis() - millis_time < 50000)) {
				delay(500);
				Serial.print(".");
			}
			if (WiFi.status() == WL_CONNECTED) {
				digitalWrite(stateled, HIGH);
				Serial.println("\n连接成功->"+WiFi.SSID()+"\nIP 地址: "+WiFi.localIP().toString());
                DynamicJsonDocument doc(512);
                doc["ip"] = WiFi.localIP().toString();
                jsonSend(1, "连接成功", doc);
                if(WiFi_State==0) this->writeConfig("wifi_state", 1);
                writeConfig("wifi_ssid", ssid);
                writeConfig("wifi_password", password);
                /* 取消注释后 配网成功后直接重启
                WiFi.softAPdisconnect(); 
                delay(1000);
                ESP.restart();
                */
			} else {
                jsonSend(0, "链接失败");
			}
		} else {
            jsonSend(0, "密码错误");
		}
	} else {
        jsonSend(0, "参数错误");
	}
    return;
}

void DEUSIT_WIFI::wifiAuto()
{
    String ssid = readConfig("wifi_ssid");
    String password = readConfig("wifi_password");
    if(ssid){
        Serial.println("找到配置，连接WIFI  ==>  "+ssid);
        WiFi.begin(ssid, password);
        delay(500);
        unsigned millis_time = millis();
        while ((WiFi.status() != WL_CONNECTED) && (millis() - millis_time < 30000)) {
            delay(250);
            ESP.wdtFeed();  //喂狗
            Serial.print("📡");
        }
        if (WiFi.status() == WL_CONNECTED) {
            Serial.print("\nIP 地址: ");
            Serial.println(WiFi.localIP());
            digitalWrite(stateled, HIGH);
        } else {
            Serial.println("连接失败!");
            Serial.println("SSID或密钥可能失效!");
            digitalWrite(stateled, LOW);
            delay(5000);
        }
    }else{
        writeConfig("wifi_state", 0);
        Serial.println("配置失效，请重新配置WIFI");
    }
}

void DEUSIT_WIFI::wifiReset()
{
    Serial.println("长按3秒后重置");
    bool res_state = true;
    unsigned int res_time = millis();
    while (millis() - res_time < 3000)
    {
        ESP.wdtFeed();  // 喂狗
        if (digitalRead(rstb) != LOW)
        {
            res_state = false;
            Serial.println("终止重置");
            break;
        }
    }
    if (res_state == true)
    {
        //删除配置文件
        //LittleFS.remove("/config.json");
        if (WiFi.status() == WL_CONNECTED)
        {
            WiFi.disconnect(true);
        }
        Serial.println("等待重启");
        for (int i = 0; i < 10; i++)
        {
            digitalWrite(stateled, LOW);
            res_time = millis();
            while (millis() - res_time < 200)
                ;
            digitalWrite(stateled, HIGH);
            res_time = millis();
            while (millis() - res_time < 200)
                ;
        }
        Serial.println("重置!");
        ESP.restart();
    }
}

void DEUSIT_WIFI::mqttAuto()
{
    if (!mqttClient.connected()) {
        String mqtt_host = readConfig("mqtt_host");
        if(!mqtt_host.isEmpty() && mqtt_host != "null"){
            Serial.println("找到MQTT配置，启动MQTT服务");
            uint16_t mqtt_port = readConfig("mqtt_port").isEmpty()?1883:readConfig("mqtt_port").toInt();
            mqttClient.setServer(mqtt_host.c_str(), mqtt_port);
            mqttClient.setCallback(mqttSub);
            Serial.println("正在尝试连接MQTT服务器...");
            String clientId = String(hostName)=="Deusit_ESP8266"?WiFi.macAddress():hostName;
            String mqtt_username = readConfig("mqtt_username");
            String mqtt_password = readConfig("mqtt_password");
            if (mqttClient.connect(clientId.c_str(), mqtt_username.c_str(), mqtt_password.c_str())) {
                Serial.println("MQTT服务器连接成功");
                String mqtt_topic = readConfig("mqtt_topic");
                if(!mqtt_topic.isEmpty()){
                    Serial.println("订阅主题  ==>  "+mqtt_topic);
                    mqttClient.subscribe(mqtt_topic.c_str());
                }
            } else {
                Serial.print("连接失败, rc=");
                Serial.print(mqttClient.state());
                Serial.println(", 5s后将再次连接");
            }
        }
    }
}

void DEUSIT_WIFI::mqttRefresh()
{
    String configStr = readConfig();
    DynamicJsonDocument configDoc(512);
    deserializeJson(configDoc, configStr);
    
    DynamicJsonDocument doc(512);
    doc["host"] = configDoc["mqtt_host"];
    doc["port"] = configDoc["mqtt_port"];
    doc["path"] = configDoc["mqtt_path"];
    doc["username"] = configDoc["mqtt_username"];
    doc["password"] = configDoc["mqtt_password"];
    doc["topic"] = configDoc["mqtt_topic"];
    doc["retain"] = configDoc["mqtt_retain"]=="true"?true:false;
    doc["status"] = this->mqttClient.connected();
    Serial.println("获取MQTT配置");
    jsonSend(1, "获取MQTT配置成功", doc);
}

void DEUSIT_WIFI::mqttSubmit()
{
    if (webServer.hasArg("host")) {
        writeConfig("mqtt_host",  webServer.arg("host"));
        writeConfig("mqtt_port",  webServer.arg("port"));
        writeConfig("mqtt_path",  webServer.arg("path"));
        writeConfig("mqtt_username",  webServer.arg("username"));
        writeConfig("mqtt_password",  webServer.arg("password"));
        writeConfig("mqtt_topic",  webServer.arg("topic"));
        writeConfig("mqtt_retain",  webServer.arg("retain"));
        mqttClient.disconnect();
        jsonSend(1, "更新MQTT配置成功");
    }else{
        jsonSend(0, "参数错误，地址不能为空");
    }
}

void DEUSIT_WIFI::opera()
{
    if (webServer.arg("act") == "reboot")
    {
        jsonSend(1, "重启成功");
        Serial.println("重启设备");
        WiFi.softAPdisconnect();
        ESP.restart();
    }
    if (webServer.arg("act") == "config")
    {
        DynamicJsonDocument doc(512);
        deserializeJson(doc, readConfig());
        jsonSend(1, "获取成功成功", doc);
    }
    if (webServer.arg("act") == "setConfig")
    {
        if(webServer.arg("state")){
            writeConfig("wifi_state",  webServer.arg("state"));
            jsonSend(1, "设置成功");
            if(webServer.arg("state") == "1"){
                WiFi.mode(WIFI_AP_STA);
                WiFi.softAP(this->APName);
                Serial.println("建立AP  ==>  "+String(this->APName));
                /*WiFi.softAPdisconnect();
                    delay(1000);
                    ESP.restart();*/
            }else if(webServer.arg("state") == "2"){
                WiFi.mode(WIFI_STA);
                Serial.println("关闭AP");
            }
        }
        
    }
}

void DEUSIT_WIFI::wifiInit()
{
    Serial.println("");
    if(!LittleFS.begin()){
        Serial.println("LITTLEFS Mount Failed");
        return;
    }
    WiFi.setAutoConnect(true);
    WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
    //attachInterrupt(digitalPinToInterrupt(rstb), wifiReset, FALLING);
    WiFi.hostname(hostName);
    pinMode(stateled, OUTPUT);
    WiFi_State = readConfig("wifi_state").toInt();
    delay(300);
    if (WiFi_State == 0)                //未配置WIFI
    {
        digitalWrite(stateled, LOW);
        WiFi.disconnect(true);
		Serial.println("\n启动WiFi配置");
		WiFi.mode(WIFI_AP_STA);
		WiFi.softAP(APName);
        Serial.println("建立AP  ==>  "+String(APName));
    }else if(WiFi_State == 1){          //已配置WIFI
        digitalWrite(stateled, LOW);
		Serial.println("\n启动WiFi配置");
		WiFi.mode(WIFI_AP_STA);
		WiFi.softAP(APName);
		Serial.println("建立AP  ==>  "+String(APName));
        wifiAuto();
    }else if(WiFi_State == 2){          //隐藏WIFI
        wifiAuto();
    }else{
        Serial.println("首次启动");
        writeConfig("wifi_state", 0);
        delay(300);
        Serial.println("请重置系统！");
        while (1)
        {
            digitalWrite(stateled, LOW);
            delay(250);
            digitalWrite(stateled, HIGH);
            delay(250);
        }
    }
    
    Serial.println("正在启动HTTP服务");
    //webServer.on("/", wwwroot);
    webServer.on("/", HTTP_GET, [this](){wwwroot();});
    webServer.on("/deviceInfo", [this](){deviceInfo(true);});
    webServer.on("/wifiConfig", [this](){wifiConfig();});
    webServer.on("/wifiScan", [this](){wifiScan();});
    webServer.on("/mqttRefresh", [this](){mqttRefresh();});
    webServer.on("/mqttSubmit", [this](){mqttSubmit();});
    webServer.on("/opera", [this](){opera();});
    webServer.onNotFound([this](){
        String path = webServer.uri();
        webServer.sendHeader("Cache-Control", "max-age=3600");
        //handleFileRead(path);   // 使用缓存发送文件
        if (LittleFS.exists(path)) {
            File file = LittleFS.open(path, "r");
            webServer.streamFile(file, getContentType(path));
            file.close();
        } else {
            webServer.send(404, "text/plain;charset=UTF-8", "404");
        }
    });
    dnsServer.start(DNS_PORT, "*", apIP);
    webServer.begin();
    Serial.println("HTTP服务启动完成");
    deviceInfo();
    Serial.println("初始化完成，加载用户程序");
}

void DEUSIT_WIFI::wifiHeart(DEUSIT_WIFI* instance)
{
    MDNS.update();
    dnsServer.processNextRequest();
    webServer.handleClient();
    instance->mqttClient.loop();
    instance->mqttAuto();
    Serial.println("tttt1");
}


DEUSIT_WIFI::DEUSIT_WIFI(const char* dName, const char* hName, const char* AName)
{
    deviceName = dName;
    hostName = hName;
    APName = AName;
}

void DEUSIT_WIFI::begin()
{
    wifiInit();
    mqttTimer.attach(1, wifiHeart, this);
}

void DEUSIT_WIFI::mqttSub(char* topic, byte* payload, unsigned int length){

}
void DEUSIT_WIFI::mqttPub(){
    
}